package com.example.utils.util;

import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;


/**
 * RSA 工具类
 *
 * @Description: 非对称加密的特点是有两把钥匙，公钥和私钥。公钥加密只能私钥解密；私钥加密只能公钥解密。
 * 在公开密钥密码体制中，加密密钥（即公开密钥）PK是公开信息，而解密密钥（即秘密密钥）SK是需要保密的。
 * 加密算法E和解密算法D也都是公开的。虽然解密密钥SK是由公开密钥PK决定的，但却不能根据PK计算出SK。
 * RSA公钥加密算法是1977年由罗纳德·李维斯特（Ron Rivest）、阿迪·萨莫尔（Adi Shamir）和伦纳德·阿德曼（Leonard Adleman）一起提出的。
 * 1987年首次公布，当时他们三人都在麻省理工学院工作。RSA就是他们三人姓氏开头字母拼在一起组成的。
 * 这种加密算法的特点主要是密钥的变化。DES只有一个密钥，相当于只有一把钥匙，如果这把钥匙丢了，数据也就不安全了。
 * RSA同时有两把钥匙，公钥与私钥。同时支持数字签名。数字签名的意义在于，对传输过来的数据进行校验，确保数据在传输过程中不被修改。
 * @ClassName RSAUtil
 * @Date 2021/9/10 下午 16:58
 */
public class RSAUtil {

    // 指定算法
    static final String A = "RSA";
    static final String B = "SHA256withRSA";
    static String publicKey = null;
    static String privateKey = null;

    public static void main(String[] args) throws Exception {
        //Java非对称加密算法--RSA
        //非对称加密的特点是有两把钥匙，公钥和私钥。公钥加密只能私钥解密；私钥加密只能公钥解密。
        // 生成公钥和私钥
        long LoginTime = System.currentTimeMillis();
        System.out.println(LoginTime);

        generatorKeyPair();
        String source = "admin";
        System.out.println("加密前的数据：\r\n" + source);

        System.out.println("--------------------------公钥加密，私钥解密------------------------------");
        // 公钥加密
        String target = encryptionByPublicKey(source);
        // 私钥解密
        decryptionByPrivateKey(target);
        long brunTime = System.currentTimeMillis();
        System.out.println(brunTime);
        System.out.println("用时=" + (brunTime - LoginTime));
        System.out.println("--------------------------私钥加密并且签名，公钥验证签名并且解密------------------------------");
        // 私钥加密
        target = encryptionByPrivateKey(source);
        // 签名
        String sign = signByPrivateKey(target);
        // 验证签名
        verifyByPublicKey(target, sign);
        // 公钥解密
        decryptionByPublicKey(target);
        long beginTime = System.currentTimeMillis();
        System.out.println(beginTime);
        System.out.println("总用时=" + (beginTime - LoginTime));


    }


    /**
     * TODO 生成密钥对
     *
     * @date 2021/9/10 下午 17:01
     */
    static void generatorKeyPair() throws Exception {
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(A);
        keyPairGen.initialize(1024);
        KeyPair keyPair = keyPairGen.generateKeyPair();
        RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
        byte[] keyBs = rsaPublicKey.getEncoded();
        publicKey = encodeBase64(keyBs);
        System.out.println("生成的公钥：\r\n" + publicKey);
        RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
        keyBs = rsaPrivateKey.getEncoded();
        privateKey = encodeBase64(keyBs);
        System.out.println("生成的私钥：\r\n" + privateKey);
    }

    /**
     * TODO 获取公钥
     *
     * @date 2021/9/10 下午 17:01
     */
    static PublicKey getPublicKey() throws Exception {
        X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(decodeBase64(publicKey));
        KeyFactory keyFactory = KeyFactory.getInstance(A);
        return keyFactory.generatePublic(publicKeySpec);
    }

    /**
     * TODO 获取私钥
     *
     * @date 2021/9/10 下午 17:02
     */
    static PrivateKey getPrivateKey() throws Exception {
        PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(decodeBase64(privateKey));
        KeyFactory keyFactory = KeyFactory.getInstance(A);
        return keyFactory.generatePrivate(privateKeySpec);
    }

    /**
     * TODO 公钥加密
     *
     * @date 2021/9/10 下午 17:02
     */
    static String encryptionByPublicKey(String source) throws Exception {
        PublicKey publicKey = getPublicKey();
        Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        cipher.update(source.getBytes(StandardCharsets.UTF_8));
        System.out.println("公钥加密后的数据未base64：\r\n" + cipher);
        String target = encodeBase64(cipher.doFinal());
        System.out.println("公钥加密后的数据：\r\n" + target);
        return target;
    }

    /**
     * TODO 公钥解密
     *
     * @date 2021/9/10 下午 17:03
     */
    static void decryptionByPublicKey(String target) throws Exception {
        PublicKey publicKey = getPublicKey();
        Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        cipher.update(decodeBase64(target));
        String source = new String(cipher.doFinal(), StandardCharsets.UTF_8);
        System.out.println("公钥解密后的数据：\r\n" + source);
    }

    /**
     * TODO 公钥验证签名
     *
     * @date 2021/9/10 下午 17:03
     */
    static void verifyByPublicKey(String target, String sign) throws Exception {
        PublicKey publicKey = getPublicKey();
        Signature signature = Signature.getInstance(B);
        signature.initVerify(publicKey);
        signature.update(target.getBytes(StandardCharsets.UTF_8));
        if (signature.verify(decodeBase64(sign))) {
            System.out.println("签名正确！");
        } else {
            System.out.println("签名错误！");
        }
    }

    /**
     * TODO 私钥加密
     *
     * @date 2021/9/10 下午 17:03
     */
    static String encryptionByPrivateKey(String source) throws Exception {
        PrivateKey privateKey = getPrivateKey();
        Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        cipher.update(source.getBytes(StandardCharsets.UTF_8));
        String target = encodeBase64(cipher.doFinal());
        System.out.println("私钥加密后的数据：\r\n" + target);
        return target;
    }

    /**
     * TODO  私钥解密
     *
     * @date 2021/9/10 下午 17:03
     */
    static void decryptionByPrivateKey(String target) throws Exception {
        PrivateKey privateKey = getPrivateKey();
        Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        cipher.update(decodeBase64(target));
        String source = new String(cipher.doFinal(), StandardCharsets.UTF_8);
        System.out.println("私钥解密后的数据：\r\n" + source);
    }

    /**
     * TODO 私钥签名
     *
     * @date 2021/9/10 下午 17:04
     */
    static String signByPrivateKey(String target) throws Exception {
        PrivateKey privateKey = getPrivateKey();
        Signature signature = Signature.getInstance(B);
        signature.initSign(privateKey);
        signature.update(target.getBytes(StandardCharsets.UTF_8));
        String sign = encodeBase64(signature.sign());
        System.out.println("生成的签名：\r\n" + sign);
        return sign;
    }

    /**
     * TODO base64编码
     *
     * @date 2021/9/10 下午 17:04
     */
    public static String encodeBase64(byte[] source) {
        return new String(Base64.encodeBase64(source), StandardCharsets.UTF_8);
    }

    /**
     * TODO Base64解码
     *
     * @date 2021/9/10 下午 17:04
     */
    public static byte[] decodeBase64(String target) {
        return Base64.decodeBase64(target.getBytes(StandardCharsets.UTF_8));
    }


}
